/*Copyright ©2015 TommyLemon(https://github.com/TommyLemon)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
limitations under the License.*/

package zuo.biao.library.manager;

import com.alibaba.fastjson.JSON;

import ohos.app.Context;
import ohos.data.DatabaseHelper;
import ohos.data.preferences.Preferences;
import zuo.biao.library.base.BaseApplication;
import zuo.biao.library.util.DataKeeper;
import zuo.biao.library.util.Log;
import zuo.biao.library.util.StringUtil;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * 磁盘缓存管理类
 *
 * @author Lemon
 * @use CacheManager.getInstance().xxxMethod(...);具体参考.BaseListActivity
 */
public class CacheManager {
    private static final String TAG = "CacheManager";

    /**
     * 缓存路径
     */
    public static final String CACHE_PATH = DataKeeper.ROOT_SHARE_PREFS_ + "CACHE_PATH";

    private Context context;

    private CacheManager(Context context) {
        this.context = context;
    }

    private static CacheManager instance;

    public static CacheManager getInstance() {
        if (instance == null) {
            synchronized (CacheManager.class) {
                if (instance == null) {
                    instance = new CacheManager(BaseApplication.getInstance());
                }
            }
        }
        return instance;
    }

    /**
     * getClassPath
     *
     * @param clazz clazz
     * @return String
     */
    public <T> String getClassPath(Class<T> clazz) {
        return clazz == null ? null : CACHE_PATH + clazz.getName();
    }

    /**
     * getListPath
     *
     * @param clazz clazz
     * @return String
     */
    public <T> String getListPath(Class<T> clazz) {
        String classPath = getClassPath(clazz);
        return StringUtil.isNotEmpty(classPath, true) ? classPath + KEY_LIST : null;
    }

    /**
     * getGroupPath
     *
     * @param clazz clazz
     * @return String
     */
    public <T> String getGroupPath(Class<T> clazz) {
        String classPath = getClassPath(clazz);
        return StringUtil.isNotEmpty(classPath, true) == false ? null : classPath + KEY_GROUP;
    }

    private Preferences getSharedPreferences(String path) {
        DatabaseHelper databaseHelper = new DatabaseHelper(context); // context 入参类型为 ohos.app.Context
        Preferences preferences = databaseHelper.getPreferences(path);
        return StringUtil.isNotEmpty(path, true) == false ? null : preferences;
    }

    /**
     * 数据列表
     */
    public static final String KEY_LIST = "LIST";

    /**
     * 数据分组,自定义
     */
    public static final String KEY_GROUP = "GROUP";
    /**
     * 分组中列表每页最大数量
     */
    public static final int MAX_PAGE_SIZE = 10;

    /**
     * 获取列表
     *
     * @param clazz clazz
     * @return list
     */
    public <T> List<T> getAllList(Class<T> clazz) {
        return getList(clazz, -1, 0);
    }

    /**
     * 获取列表
     *
     * @param clazz clazz
     * @param start start
     * @param pageSize pageSize
     * @return getList
     */
    public <T> List<T> getList(Class<T> clazz, int start, int pageSize) {
        return getList(clazz, null, start, pageSize);
    }

    /**
     * 获取列表
     *
     * @param clazz clazz
     * @param group group
     * @return List
     */
    public <T> List<T> getAllList(Class<T> clazz, String group) {
        return StringUtil.isNotEmpty(group, true) ? getList(clazz, group, -1, 0) : null;
    }

    /**
     * 获取列表
     *
     * @param clazz clazz
     * @param group == null ? all : in group
     * @param start < 0 ? all in group : subList(start, end)
     * @param count > 0 ? all in group : subList(start, end)
     * @return List
     */
    public <T> List<T> getList(Class<T> clazz, String group, int start, int count) {
        Log.info(TAG, "\n\n<<<<<<<<<<<<<<<<\ngetList  group = " + group + "; start = " + start + "; count = " + count);
        if (count <= 0 || clazz == null) {
            Log.error(TAG, "getList  count <= 0 || clazz == null >> return null;");
            return null;
        }
        Cache<T> cacheList = new Cache<T>(clazz, context, getClassPath(clazz) + KEY_LIST);

        if (StringUtil.isNotEmpty(group, true) == false) {
            return cacheList == null ? null : cacheList.getValueList(start, start + count);
        }

        List<String> idList = getIdList(clazz, group);
        final int totalCount = idList == null ? 0 : idList.size();
        Log.info(TAG, "getList  idList.size() = " + totalCount);
        if (totalCount <= 0) {
            Log.error(TAG, "getList  totalCount <= 0 >> return null;");
            return null;
        }

        if (start >= 0) {
            Log.info(TAG, "getList  start >= 0 >> ");

            int end = start + count;
            if (end > totalCount) {
                end = totalCount;
            }
            Log.info(TAG, "getList  end = " + end);
            if (end <= start) {
                Log.error(TAG, "getList  end <= start >> return null;");
                return null;
            }

            if (start > 0 || end < totalCount) {
                Log.info(
                        TAG,
                        "getList  start > 0 || end < totalCount  >> idList = idList.subList("
                                + start
                                + ","
                                + end
                                + "); >>");
                idList = idList.subList(start, end);
            }
        }

        List<T> list = new ArrayList<T>();
        T data;
        for (String id : idList) {
            data = cacheList.get(id);
            if (data != null) {
                list.add(data);
            }
        }

        Log.info(TAG, "getList  return list; list.size() = " + list.size() + ">>>>>>>>>>>>>>>>>>>>>>");

        return list;
    }

    /**
     * 获取单个值
     *
     * @param clazz clazz
     * @param id id
     * @return t
     */
    public <T> T get(Class<T> clazz, String id) {
        Cache<T> cacheList = clazz == null ? null : new Cache<T>(clazz, context, getClassPath(clazz) + KEY_LIST);
        return cacheList == null ? null : cacheList.get(id);
    }

    /**
     * 获取id列表
     *
     * @param clazz clazz
     * @param group group
     * @return List
     */
    public <T> List<String> getIdList(Class<T> clazz, String group) {
        Preferences sp = getSharedPreferences(getClassPath(clazz) + KEY_GROUP);
        return sp == null ? null : JSON.parseArray(sp.getString(StringUtil.getTrimedString(group), null), String.class);
    }

    /**
     * 保存列表
     *
     * @param clazz 类
     * @param map   数据表
     */
    public <T> void addList(Class<T> clazz, LinkedHashMap<String, T> map) {
        saveList(clazz, null, map, 0, 0);
    }

    /**
     * 添加列表
     *
     * @param clazz 类
     * @param group 分组
     * @param map   数据表
     */
    public <T> void addList(Class<T> clazz, String group, LinkedHashMap<String, T> map) {
        addList(clazz, group, map, -1);
    }

    /**
     * 添加列表
     *
     * @param clazz    类
     * @param group    分组
     * @param map      数据表
     * @param pageSize 每页大小
     */
    public <T> void addList(Class<T> clazz, String group, LinkedHashMap<String, T> map, int pageSize) {
        if (StringUtil.isNotEmpty(group, true) == false) {
            Log.error(TAG, "addList  StringUtil.isNotEmpty(group, true) == false >> return;");
            return;
        }
        saveList(clazz, group, map, -1, pageSize);
    }

    /**
     * 保存列表
     *
     * @param clazz    类
     * @param group    分组
     * @param map      数据表
     * @param start    存储起始位置,[start, start + map.size()]中原有的将被替换. start = start < 0 ? idList.size() : start;
     * @param pageSize 每页大小
     */
    public <T> void saveList(Class<T> clazz, String group, LinkedHashMap<String, T> map, int start, int pageSize) {
        if (clazz == null || map == null || map.size() <= 0) {
            Log.error(TAG, "saveList  clazz == null || map == null || map.size() <= 0 >> return;");
            return;
        }
        String classPath = getClassPath(clazz);

        if (StringUtil.isNotEmpty(group, true)) {
            group = StringUtil.getTrimedString(group);
            List<String> newIdList = new ArrayList<String>(map.keySet()); // 用String而不是Long，因为订单Order的id超出Long的最大值
            // 保存至分组<<<<<<<<<<<<<<<<<<<<<<<<<
            Preferences sp = getSharedPreferences(classPath + KEY_GROUP);
            // 列表每页大小
            if (pageSize > 0) {
                if (pageSize > MAX_PAGE_SIZE) {
                    pageSize = MAX_PAGE_SIZE;
                }
            }
            // id列表
            List<String> idList = JSON.parseArray(sp.getString(group, null), String.class);
            if (idList == null) {
                idList = new ArrayList<String>();
            }
            if (start < 0) {
                start = idList.size();
            }
            String id;
            for (int i = start; i < start + newIdList.size(); i++) {
                id = newIdList.get(i - start);
                if (id == null || id.isEmpty()) {
                    continue;
                }
                if (idList.contains(id)) {
                    idList.remove(id); // 位置发生变化
                }
                if (i < idList.size()) {
                    idList.set(i, id);
                } else {
                    idList.add(id);
                }
            }
            sp.delete(group);
            sp.putString(group, JSON.toJSONString(idList));
            sp.flushSync();
        }
        Cache<T> cache = new Cache<T>(clazz, context, classPath + KEY_LIST);
        cache.saveList(map);
    }

    /**
     * 保存
     * 未完成
     *
     * @param clazz clazz
     * @param data  数据
     * @param id id
     */
    public <T> void save(Class<T> clazz, T data, String id) {
        save(clazz, data, id, null);
    }

    /**
     * ROOT
     * 保存
     *
     * @param clazz clazz
     * @param data  数据
     * @param id id
     * @param group 分组
     */
    public <T> void save(Class<T> clazz, T data, String id, String group) {
        if (data == null || StringUtil.isNotEmpty(id, true) == false) {
            Log.error(id, "save  data == null || StringUtil.isNotEmpty(id, true) == false  >>  return;");
            return;
        }

        new Cache<T>(clazz, context, getListPath(clazz)).save(id, data);

        Preferences sp = getSharedPreferences(getGroupPath(clazz));
        if (sp != null) {
            group = StringUtil.getTrimedString(group);

            Log.info(TAG, "save sp != null >> save to group");
            List<String> idList = getIdList(clazz, group);
            if (idList == null) {
                idList = new ArrayList<String>();
            }
            if (idList.contains(id) == false) {
                Log.info(TAG, "save idList.contains(id) == false >> add");
                idList.add(0, id);
                sp.delete(group).putString(group, JSON.toJSONString(idList));
                sp.flushSync();
            }
        }
    }

    /**
     * 清空类
     *
     * @param <T> <T>
     * @param clazz clazz
     */
    public <T> void clear(Class<T> clazz) {
        clear(getSharedPreferences(getListPath(clazz)));
    }

    /**
     * 清空群组
     *
     * @param <T> <T>
     * @param clazz clazz
     * @param group group
     */
    public <T> void clear(Class<T> clazz, String group) {
        clear(clazz, group, false);
    }

    /**
     * 清空群组
     *
     * @param clazz clazz
     * @param group group
     * @param removeAllInGroup 删除群组内所有id对应数据
     * @param <T> <T>
     */
    public <T> void clear(Class<T> clazz, String group, boolean removeAllInGroup) {
        Log.info(TAG, "clear  group = " + group + "; removeAllInGroup = " + removeAllInGroup);
        List<String> list = removeAllInGroup == false ? null : getIdList(clazz, group);
        if (list != null) {
            Cache<T> cache = new Cache<T>(clazz, context, getListPath(clazz));
            for (String id : list) {
                cache.remove(id);
            }
        }
        clear(getSharedPreferences(getGroupPath(clazz)));
    }

    /**
     * 清空
     *
     * @param sp sp
     */
    public void clear(Preferences sp) {
        if (sp == null) {
            Log.error(TAG, "clearList  sp == null >> return;");
            return;
        }
        sp.clear();
    }

    /**
     * 移除
     *
     * @param clazz c
     * @param id    i
     * @param <T>   t
     */
    public <T> void remove(Class<T> clazz, String id) {
        if (clazz == null) {
            Log.error(TAG, "remove  clazz == null >> return;");
            return;
        }
        new Cache<T>(clazz, context, getListPath(clazz)).remove(id);
    }
}
